Towers of Hanoi animation¶
Note
A minimal ‘Towers of Hanoi’ animation: A tower of 6 discs is transferred from the left to the right peg. An imho quite elegant and concise implementation using a tower class, which is derived from the built-in type list. Discs are turtles with shape “square”, but stretched to rectangles by shapesize()
Suppose you have a tower of five disks, originally on peg one. If you already knew how to move a tower of four disks to peg two, you could then easily move the bottom disk to peg three, and then move the tower of four from peg two to peg three. But what if you do not know how to move a tower of height four? Suppose that you knew how to move a tower of height three to peg three; then it would be easy to move the fourth disk to peg two and move the three from peg three on top of it. But what if you do not know how to move a tower of three? How about moving a tower of two disks to peg two and then moving the third disk to peg three, and then moving the tower of height two on top of it? But what if you still do not know how to do this? Surely you would agree that moving a single disk to peg three is easy enough, trivial you might even say. This sounds like a base case in the making.
#!/usr/bin/env python
from turtle import *
class Disc(Turtle):
def __init__(self, n):
Turtle.__init__(self, shape="square", visible=False)
self.pu()
self.shapesize(1.5, n*1.5, 2) # square-->rectangle
self.fillcolor(n/6., 0, 1-n/6.)
self.st()
class Tower(list):
''' Hanoi tower, a subclass of built-in type list '''
def __init__(self, x, name):
"create an empty tower. x is x-position of peg"
self.x = x
self.name = name
def push(self, d):
d.setx(self.x)
d.sety(-150 + 34*len(self))
self.append(d)
def pop(self):
d = list.pop(self)
d.sety(150)
return d
def erasableWrite(name, font, align, reuse=None):
eraser = Turtle() if reuse is None else reuse
eraser.hideturtle()
eraser.up()
eraser.setposition(position())
eraser.write(name, font=font, align=align)
return eraser
# recursion
def move_tower(height, from_pole, to_pole, with_pole):
''' Move a tower from the starting pole, to the goal pole, using an intermediate pole '''
if height > 0:
# Move a tower of height-1 to an intermediate pole, using the final pole
# er = erasableWrite("Moving a Tower to Intermediate pole using the Final pole",
# font=("Courier", 16, "bold"),
# align="center")
move_tower(height - 1, from_pole, with_pole, to_pole)
# Move the remaining disk to the final pole
# er.clear()
# er = erasableWrite("Moving the remaining Disk from Starting pole to Final pole",
# font=("Courier", 16, "bold"),
# align="center")
to_pole.push(from_pole.pop())
# Move the tower of height-1 from the intermediate pole
# to the final pole using the original pole
# er.clear()
# er = erasableWrite("Moving the Tower from Intermediate pole to Final pole using the Original pole",
# font=("Courier", 16, "bold"),
# align="center")
move_tower(height - 1, with_pole, to_pole, from_pole)
def play():
onkey(None,"space")
clear()
move_tower(6, t1, t3, t2)
write("press STOP button to exit",
align="center",
font=("Courier", 16, "bold"))
def main():
global t1, t2, t3
ht(); penup(); goto(0, -225) # writer turtle
t1 = Tower(-250, "Start") # from
t2 = Tower(0, "Inter") # with
t3 = Tower(250, "Goal") # to
# make tower of 6 discs
for i in range(6,0,-1):
t1.push(Disc(i))
# prepare spartanic user interface ;-)
write("press spacebar to start game",
align="center",
font=("Courier", 16, "bold"))
onkey(play, "space")
listen()
return "EVENTLOOP"
if __name__=="__main__":
msg = main()
print(msg)
mainloop()